package com.sms.util;

public class Base64 {

    static final String encodingChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    private Base64() {
    }

    public static String encode(String source) {
        char sourceBytes[] = getPaddedBytes(source);
        int numGroups = (sourceBytes.length + 2) / 3;
        char targetBytes[] = new char[4];
        char target[] = new char[4 * numGroups];
        for (int group = 0; group < numGroups; group++) {
            convert3To4(sourceBytes, group * 3, targetBytes);
            for (int i = 0; i < targetBytes.length; i++)
                target[i + 4 * group] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
                        .charAt(targetBytes[i]);

        }

        int numPadBytes = sourceBytes.length - source.length();
        for (int i = target.length - numPadBytes; i < target.length; i++)
            target[i] = '=';

        return new String(target);
    }

    private static char[] getPaddedBytes(String source) {
        char converted[] = source.toCharArray();
        int requiredLength = 3 * ((converted.length + 2) / 3);
        char result[] = new char[requiredLength];
        System.arraycopy(converted, 0, result, 0, converted.length);
        return result;
    }

    private static void convert3To4(char source[], int sourceIndex,
            char target[]) {
        target[0] = (char) (source[sourceIndex] >>> 2);
        target[1] = (char) ((source[sourceIndex] & 3) << 4 | source[sourceIndex + 1] >>> 4);
        target[2] = (char) ((source[sourceIndex + 1] & 0xf) << 2 | source[sourceIndex + 2] >>> 6);
        target[3] = (char) (source[sourceIndex + 2] & 0x3f);
    }

    public static String decode(String source) {
        if (source.length() % 4 != 0)
            throw new RuntimeException(
                    "valid Base64 codes have a multiple of 4 characters");
        int numGroups = source.length() / 4;
        int numExtraBytes = source.endsWith("==") ? 2 : ((int) (source
                .endsWith("=") ? 1 : 0));
        byte targetBytes[] = new byte[3 * numGroups];
        byte sourceBytes[] = new byte[4];
        for (int group = 0; group < numGroups; group++) {
            for (int i = 0; i < sourceBytes.length; i++)
                sourceBytes[i] = (byte) Math.max(0,
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
                                .indexOf(source.charAt(4 * group + i)));

            convert4To3(sourceBytes, targetBytes, group * 3);
        }

        return new String(targetBytes, 0, targetBytes.length - numExtraBytes);
    }

    private static void convert4To3(byte source[], byte target[],
            int targetIndex) {
        target[targetIndex] = (byte) (source[0] << 2 | source[1] >>> 4);
        target[targetIndex + 1] = (byte) ((source[1] & 0xf) << 4 | source[2] >>> 2);
        target[targetIndex + 2] = (byte) ((source[2] & 3) << 6 | source[3]);
    }
}